package kz.pompei.ifacer.testers;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;

import kz.pompei.ifacer.cursor_manager.CursorManager;
import kz.pompei.ifacer.cursor_manager.def.DefCursorManager;
import kz.pompei.ifacer.model.CursorInfo;
import kz.pompei.ifacer.model.CursorKind;
import kz.pompei.ifacer.model.PaintKind;
import kz.pompei.ifacer.model.widgets.Align;
import kz.pompei.ifacer.model.widgets.CheckState;
import kz.pompei.ifacer.model.widgets.Widget;
import kz.pompei.ifacer.model.widgets.WidgetButton;
import kz.pompei.ifacer.model.widgets.WidgetCheck;
import kz.pompei.ifacer.model.widgets.WidgetForm;
import kz.pompei.ifacer.model.widgets.WidgetGrid;
import kz.pompei.ifacer.model.widgets.WidgetLabel;
import kz.pompei.ifacer.visio.Visio;
import kz.pompei.ifacer.visio.def.DefVisio;

public class DefVisioTester extends JPanel {
  private PaintKind paintKind = PaintKind.NORMAL;
  
  private final Visio visio = new DefVisio();
  
  private Runnable updatePaintKind = null;
  
  private final WidgetForm form;
  private final WidgetLabel label;
  private final WidgetButton button;
  private final WidgetGrid grid;
  private final WidgetCheck check;
  private final List<Widget> widgets = new ArrayList<>();
  
  private Point mouse = new Point(0, 0);
  
  private final CursorManager cursorManager = new DefCursorManager();
  
  public DefVisioTester() {
    {
      form = new WidgetForm();
      form.x = 30;
      form.y = 20;
      form.width = 400;
      form.height = 300;
      form.title = "Заголовок";
      widgets.add(form);
    }
    {
      label = new WidgetLabel();
      label.x = 560;
      label.y = 20;
      label.side = Align.LEFT;
      label.text = "Text";
      widgets.add(label);
    }
    {
      check = new WidgetCheck();
      check.x = 560;
      check.y = 50;
      widgets.add(check);
    }
    {
      button = new WidgetButton();
      button.x = 560;
      button.y = 80;
      button.caption = "Click";
      widgets.add(button);
    }
    {
      grid = new WidgetGrid();
      grid.x = 560;
      grid.y = 270;
      widgets.add(grid);
    }
    addMouseMotionListener(new MouseMotionListener() {
      @Override
      public void mouseMoved(MouseEvent e) {
        mouse = e.getPoint();
        resetCursor();
        repaint(100);
      }
      
      @Override
      public void mouseDragged(MouseEvent e) {}
    });
    
    addMouseListener(new MouseListener() {
      @Override
      public void mouseReleased(MouseEvent e) {
        mouse = e.getPoint();
        resetCursor();
        repaint(100);
      }
      
      @Override
      public void mousePressed(MouseEvent e) {
        mouse = e.getPoint();
        repaint(100);
      }
      
      @Override
      public void mouseExited(MouseEvent e) {
        mouse = e.getPoint();
        repaint(100);
      }
      
      @Override
      public void mouseEntered(MouseEvent e) {
        mouse = e.getPoint();
        resetCursor();
        repaint(100);
      }
      
      @Override
      public void mouseClicked(MouseEvent e) {
        mouse = e.getPoint();
        repaint(100);
      }
    });
  }
  
  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setTitle("Тестирование визуальных элементов");
    frame.setBounds(100, 100, 1600, 600);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    final DefVisioTester pane = new DefVisioTester();
    frame.setContentPane(pane);
    
    {
      MenuBar mb = new MenuBar();
      frame.setMenuBar(mb);
      //PaintKind menu
      {
        Menu paintKindMenu = new Menu(PaintKind.class.getSimpleName());
        mb.add(paintKindMenu);
        
        pane.fillPaintKindMenu(paintKindMenu);
        pane.updatePaintKind.run();
      }
      //Form menu
      {
        Menu formMenu = new Menu("Form");
        mb.add(formMenu);
        
        pane.fillFormMenu(formMenu);
      }
      //Label menu
      {
        Menu formMenu = new Menu("Label");
        mb.add(formMenu);
        
        pane.fillLabelMenu(formMenu);
      }
      //Label menu
      {
        Menu formMenu = new Menu("Check box");
        mb.add(formMenu);
        
        pane.fillCheckBoxMenu(formMenu);
      }
      //Button menu
      {
        Menu formMenu = new Menu("Button");
        mb.add(formMenu);
        
        pane.fillButtonMenu(formMenu);
      }
    }
    frame.setVisible(true);
  }
  
  private void fillFormMenu(Menu formMenu) {
    for (final String t : new String[] {
        "Просто название",
        null,
        "Очень длинное название, Очень длинное название, Очень длинное название,"
            + " Очень длинное название, Очень длинное название, Очень длинное название, "
            + "Очень длинное название" }) {
      MenuItem mi = new MenuItem("Заголовок = " + t);
      formMenu.add(mi);
      mi.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          form.title = t;
          repaint();
        }
      });
    }
  }
  
  private void fillPaintKindMenu(Menu top) {
    class Asd {
      PaintKind pk;
      MenuItem mi;
      
      public Asd(PaintKind pk, MenuItem mi) {
        this.pk = pk;
        this.mi = mi;
        
        mi.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            paintKind = Asd.this.pk;
            updatePaintKind.run();
          }
        });
      }
    }
    
    final List<Asd> list = new ArrayList<>();
    
    for (PaintKind pk : PaintKind.values()) {
      MenuItem mi = new MenuItem();
      top.add(mi);
      list.add(new Asd(pk, mi));
    }
    
    updatePaintKind = new Runnable() {
      @Override
      public void run() {
        for (Asd asd : list) {
          boolean sel = asd.pk == paintKind;
          asd.mi.setLabel(sel ? "--> " + asd.pk.name() + "" :asd.pk.name());
          
          repaint();
        }
      }
    };
  }
  
  @Override
  public void paint(Graphics g) {
    try {
      paintInner(g);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  
  private void resetCursor() {
    try {
      resetCursorInner();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  
  private void paintInner(Graphics g) throws Exception {
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, getWidth(), getHeight());
    
    g.setColor(Color.BLACK);
    g.drawString("mouse(x, y) = (" + mouse.x + ", " + mouse.y + ")", 4, 13);
    
    Graphics2D g2 = (Graphics2D)g;
    
    g2.setColor(Color.CYAN);
    for (int x = 0; x < getWidth(); x += 20) {
      g2.drawLine(x, 0, x, getHeight());
    }
    for (int y = 0; y < getHeight(); y += 20) {
      g2.drawLine(0, y, getWidth(), y);
    }
    
    for (Widget widget : widgets) {
      visio.paint(g2, widget, paintKind, mouse, new Rectangle(0, 0, getWidth(), getHeight()));
    }
  }
  
  private void resetCursorInner() throws Exception {
    Graphics2D g2 = (Graphics2D)getGraphics();
    
    for (int i = widgets.size(); i-- > 0;) {
      Widget widget = widgets.get(i);
      
      CursorInfo cursorInfo = visio.getCursorInfo(g2, widget, mouse);
      
      if (cursorInfo != null) {
        setCursor(cursorManager.getCursor(cursorInfo));
        return;
      }
    }
    
    setCursor(cursorManager.getCursor(new CursorInfo(CursorKind.DEF, null)));
  }
  
  private void fillButtonMenu(Menu formMenu) {
    for (final String s : new String[] {
        "Короткий лэйбел",
        null,
        "Длинный при длинный лэйбел, Длинный при длинный лэйбел, Длинный при длинный лэйбел,"
            + " Длинный при длинный лэйбел, " }) {
      MenuItem mi = new MenuItem("" + s);
      formMenu.add(mi);
      mi.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          button.caption = s;
          repaint();
        }
      });
    }
  }
  
  private void fillCheckBoxMenu(Menu formMenu) {
    {
      Menu menu = new Menu("set label = ");
      formMenu.add(menu);
      for (final String s : new String[] {
          "Короткий лэйбел",
          null,
          "Длинный при длинный лэйбел, Длинный при длинный лэйбел, Длинный при длинный "
              + "лэйбел, Длинный при длинный лэйбел, " }) {
        MenuItem mi = new MenuItem("" + s);
        menu.add(mi);
        mi.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            check.label = s;
            repaint();
          }
        });
      }
    }
    {
      Menu menu = new Menu("set side = ");
      formMenu.add(menu);
      for (final Align ls : Align.values()) {
        MenuItem mi = new MenuItem(ls.name());
        menu.add(mi);
        mi.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            check.side = ls;
            repaint();
          }
        });
      }
    }
    {
      Menu menu = new Menu("set state = ");
      formMenu.add(menu);
      for (final CheckState state : CheckState.values()) {
        MenuItem mi = new MenuItem(state.name());
        menu.add(mi);
        mi.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            check.state = state;
            repaint();
          }
        });
      }
    }
  }
  
  private void fillLabelMenu(Menu formMenu) {
    {
      Menu menu = new Menu("set text = ");
      formMenu.add(menu);
      for (final String s : new String[] {
          "Короткий лэйбел",
          null,
          "Длинный при длинный лэйбел, Длинный при длинный лэйбел, Длинный при длинный лэйбел,"
              + " Длинный при длинный лэйбел, " }) {
        MenuItem mi = new MenuItem("" + s);
        menu.add(mi);
        mi.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            label.text = s;
            repaint();
          }
        });
      }
    }
    {
      Menu menu = new Menu("set side = ");
      formMenu.add(menu);
      for (final Align ls : Align.values()) {
        MenuItem mi = new MenuItem(ls.name());
        menu.add(mi);
        mi.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            label.side = ls;
            repaint();
          }
        });
      }
    }
  }
}
